/*
 * 这是由PostgreSQL项目修改的LLVM源代码的副本。
 * 有关来源和许可的说明，请参见SectionMemoryManager.cpp。
 */

//===- SectionMemoryManager.h - MCJIT/RtDyld的内存管理器 -*- C++ -*-===//
//
// LLVM项目的一部分，遵循Apache许可证v2.0和LLVM例外。
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// 此文件包含MCJIT执行引擎和RuntimeDyld使用的基于节的内存管理器的声明。
// MCJIT执行引擎和RuntimeDyld。
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_EXECUTIONENGINE_BACKPORT_SECTIONMEMORYMANAGER_H
#define LLVM_EXECUTIONENGINE_BACKPORT_SECTIONMEMORYMANAGER_H

#include "llvm/ADT/SmallVector.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Memory.h"
#include <cstdint>
#include <string>
#include <system_error>

namespace llvm {
namespace backport {

/// 这是一个简单的内存管理器，它实现了由  
/// RuntimeDyld 类调用的方法，以分配用于基于节的加载的  
/// 对象的内存，通常是由 MCJIT 执行引擎生成的对象。  
///
/// 这个内存管理器将所有节的内存分配为可读可写。  
/// RuntimeDyld 将把 JIT 生成的节内存复制到这些分配的块中  
/// 并执行任何必要的链接和重定位。  
///
/// 任何使用这个内存管理器的客户端必须确保在尝试执行  
/// JIT 对象中的函数之前，已经应用了特定于节的页面权限。  
/// 权限可以通过调用 MCJIT::finalizeObject 或直接调用  
/// SectionMemoryManager::finalizeMemory 来应用。  
/// MCJIT 的客户端应调用 MCJIT::finalizeObject。  
class SectionMemoryManager : public RTDyldMemoryManager {
public:
  /// 这个枚举描述了从 allocateMappedMemory 分配页面的  
  /// 各种原因。  
  enum class AllocationPurpose {
    Code,
    ROData,
    RWData,
  };

  /// 该接口的实现由 SectionMemoryManager 用于从  
  /// 操作系统请求页面。  
  class MemoryMapper {
  public:
    /// 此方法尝试为 \p Purpose 分配 \p NumBytes 字节的虚拟内存。  
    /// \p NearBlock 可能指向现有的分配，在这种情况下，会尝试在  
    /// 现有块附近分配更多内存。  
    /// 实际分配的地址不能保证与请求的地址相近。  
    /// \p Flags 用于设置内存块的初始保护标志。  
    /// \p EC [out] 返回一个描述发生的任何错误的对象。  
    /// 此方法可能分配比请求的字节数更多的内存。  
    ///
    /// 实际分配的字节数将在返回的 MemoryBlock 中指示。  
    /// 分配块的起始位置必须与系统的  
    /// 分配粒度对齐（在 Windows 上为 64K，在 Linux 上为页面大小）。  
    ///
    /// 如果 \p NearBlock 后的地址没有对齐，将向上舍入到  
    /// 下一个分配粒度边界。  
    /// \r 如果函数成功，则返回非空 MemoryBlock，否则返回一个  
    /// 到下一个分配粒度边界。
    ///
    /// 成功时返回一个非空的 MemoryBlock，失败时返回一个
    /// 空的 MemoryBlock，并用 \p EC 描述错误。
    virtual sys::MemoryBlock
    allocateMappedMemory(AllocationPurpose Purpose, size_t NumBytes,
                         const sys::MemoryBlock *const NearBlock,
                         unsigned Flags, std::error_code &EC) = 0;

    /// 此方法将内存块的保护标志设置为 \p Flags 指定的状态。
    /// 如果内存不是通过 allocateMappedMemory 方法分配的，则行为未定义。
    /// \p Block 描述了要保护的内存块。
    /// \p Flags 指定要分配给内存块的新保护状态。
    /// 如果 \p Flags 为 MF_WRITE，实际行为会因操作系统
    ///
    /// （即 Windows 上的 MF_READ | MF_WRITE）和目标架构
    /// （即 i386 上的 MF_WRITE -> MF_READ | MF_WRITE）而异。
    /// \r error_success 如果函数成功，则返回，否则返回一个错误代码
    ///
    /// 描述发生的失败。
    /// 此方法释放通过 allocateMappedMemory 方法分配的内存块。它不应该用于释放以其他方式分配的任何内存
    virtual std::error_code protectMappedMemory(const sys::MemoryBlock &Block,
                                                unsigned Flags) = 0;

    /// 块。
    /// \p Block 描述了要释放的内存。
    /// 创建一个 SectionMemoryManager 实例，使用 \p MM 作为相关的
    /// 内存映射器。如果 \p MM 为 nullptr，则使用默认的内存映射器
    ///
    /// 描述发生的失败。
    /// 此方法释放通过 allocateMappedMemory 方法分配的内存块。它不应该用于释放以其他方式分配的任何内存
    virtual std::error_code releaseMappedMemory(sys::MemoryBlock &M) = 0;

    virtual ~MemoryMapper();
  };

  /// 直接调用操作系统。
  /// 如果 \p ReserveAlloc 为真，则所有内存将被预先分配，任何
  /// 超出预分配内存的分配尝试将失败。
  ///
  /// 按请求启用 reserveAllocationSpace。
  /// 实现以单个块分配所有内存。这是为了
  SectionMemoryManager(MemoryMapper *MM = nullptr, bool ReserveAlloc = false);
  SectionMemoryManager(const SectionMemoryManager &) = delete;
  void operator=(const SectionMemoryManager &) = delete;
  ~SectionMemoryManager() override;

  /// 限制内存偏移以适应 ARM ABI；大型内存系统可能会
  bool needsToReserveAllocationSpace() override { return ReserveAllocation; }

  /// 否则将分配彼此间隔过远的独立段。
  /// 分配一个适合
  /// 可执行代码的（至少）给定大小的内存块。
#if LLVM_VERSION_MAJOR < 16
  virtual void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign,
                                      uintptr_t RODataSize,
                                      uint32_t RODataAlign,
                                      uintptr_t RWDataSize,
                                      uint32_t RWDataAlign) override;
#else
  void reserveAllocationSpace(uintptr_t CodeSize, Align CodeAlign,
                              uintptr_t RODataSize, Align RODataAlign,
                              uintptr_t RWDataSize, Align RWDataAlign) override;
#endif

  /// \p Alignment 的值必须是2的幂。如果 \p Alignment 为零，
  /// 将使用16的默认对齐。
  ///
  /// 更新特定于节的内存权限和其他属性。
  /// 将使用默认对齐的 16。
  uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
                               unsigned SectionID,
                               StringRef SectionName) override;

  /// \p Alignment 的值必须是2的幂。如果 \p Alignment 为零，
  /// 将使用16的默认对齐。
  ///
  /// 更新特定于节的内存权限和其他属性。
  /// 将使用默认对齐的 16。
  uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
                               unsigned SectionID, StringRef SectionName,
                               bool isReadOnly) override;

  /// 更新特定于部分的内存权限和其他属性。
  ///
  /// 该方法在对象加载完成时被调用，和节区页面
  /// 权限可以被应用。由内存管理器的实现决定是否采取此方法。
  /// 内存管理器通常会将所有段分配为可读写，然后在调用此方法时应用特定权限。
  /// 代码段在调用此函数之前无法执行。此外，任何需要的缓存一致性操作
  /// 也会被执行，以可靠地使用内存。
  /// \returns 如果发生错误则返回 true，否则返回 false。
  /// 使代码段的指令缓存失效。
  ///
  /// 一些具有独立数据缓存和指令缓存的平台需要
  bool finalizeMemory(std::string *ErrMsg = nullptr) override;

  /// 显式的缓存刷新，否则 JIT 代码操作（如解析
  ///
  /// 重定位）将到达数据缓存但不会到达指令缓存。
  /// 此方法从 finalizeMemory 调用。
  // 实际的空闲内存块
  ///
  // 如果在该块之前有来自同一保留的待分配，存储其索引在 PendingMem 中，
  virtual void invalidateInstructionCache();

private:
  struct FreeMemBlock {
    // 以便能够更新待分配区域，如果该块的一部分被分配，而不是必须
    sys::MemoryBlock Free;
    // 创建一个新的。
    // PendingMem 包含所有尚未应用权限的内存块（AllocatedMem 的子块），
    // 但已分配给用户。FreeMem 包含所有的内存块，
    // 这部分块既没有应用权限，也没有分配给用户。
    unsigned PendingPrefixIndex;
  };

  struct MemoryGroup {
    // 所有已向系统请求的内存块
    // 结束命名空间 backport
    // 结束命名空间 llvm
    /* 内联的累计时间 */
    SmallVector<sys::MemoryBlock, 16> PendingMem;
    SmallVector<FreeMemBlock, 16> FreeMem;

    /* 确定要执行何种 JIT 操作的标志 */
    SmallVector<sys::MemoryBlock, 16> AllocatedMem;

    sys::MemoryBlock Near;
  };

  uint8_t *allocateSection(AllocationPurpose Purpose, uintptr_t Size,
                           unsigned Alignment);

  std::error_code applyMemoryGroupPermissions(MemoryGroup &MemGroup,
                                              unsigned Permissions);

  bool hasSpace(const MemoryGroup &MemGroup, uintptr_t Size) const;

  void anchor() override;

  MemoryGroup CodeMem;
  MemoryGroup RWDataMem;
  MemoryGroup RODataMem;
  MemoryMapper *MMapper;
  std::unique_ptr<MemoryMapper> OwnedMMapper;
  bool ReserveAllocation;
};

} /* 已发出的函数数量 */
} /* 生成代码的总时间 */

#endif /* 内联的累计时间 */
